package model;

import java.util.HashMap;
import java.util.Timer;

import android.util.Log;
import view.ScannerActivity;
import model.location.WorldCoordinate;
import model.service.ServiceConnection;

public class OnScreen 
{	
	private Timer _timer;
	private Dimension _screenDimension;
	
	private WorldCoordinate upperLeft;
	private WorldCoordinate lowerRight;
	private float angle;
	
	//private ArrayList<ReferencePoint> _refPoints;	
	private HashMap<String, Snippet> _snippets;
	private ScannerActivity _observer;
	
	//Singleton
	//Volatile means java will always get this variable from main memory
	private static volatile OnScreen INSTANCE;
	
	private OnScreen()
	{
		_timer = new Timer();	
		//_refPoints = new ArrayList<ReferencePoint>();
		_snippets = new HashMap<String, Snippet>();
	}

	public static OnScreen getInstance()
	{
		if(INSTANCE == null)
		{
			//synchronized means only one thread can use the variable at a time (locks the class)
			synchronized (OnScreen.class) 
			{
				if(INSTANCE == null)
				{
					INSTANCE = new OnScreen();
				}
			}
		}
		return INSTANCE;
	}

	/**
	 * this method does a few things:</br>
	 * - update existing snippets that are already visible</br>
	 * - delete existing snippets that are no longer needed</br>
	 * - add new snippets that came in</br>
	 * 
	 * <p>when these functionalities are required it calls a method that does this for him.
	 * mainly this method loops through the received hashmap of snippets and decides what to do with them.</p>
	 * 
	 * @param updatedAndNewSnippets - The list of Snippets retreived from server
	 */
	public synchronized void updateSnippetList(HashMap<String, Snippet> updatedAndNewSnippets)
	{		
		try {
			// create a copy of the current snippet hashmap, needed for deleting old snippets later
			HashMap<String, Snippet> copyCurrent = new HashMap<String,Snippet>();
			copyCurrent.putAll(_snippets);
			
			Log.d("SNIPVIEW", copyCurrent.size() + "....  " + updatedAndNewSnippets.size() + "....  " + _snippets.size());
			// for every snippet in the received hashmap of snippets.
			for(String key : updatedAndNewSnippets.keySet())
			{
				Log.d("SNIPVIEW", "deze key: " + key);
				Snippet currentlyCheckingSnippet = updatedAndNewSnippets.get(key);
				
				//check if the currentlyCheckingSnippet exists
				if(copyCurrent.containsKey(key))
				{
					updateExistingSnippet(currentlyCheckingSnippet); 
					copyCurrent.remove(key);
				}
				else
				{
					addNewSnippet(currentlyCheckingSnippet);
				}
			}
			
			// delete the snippets that werent updated but are still present
			Log.d("SNIPVIEW", copyCurrent.size() + "" );
			deleteOldSnippets(copyCurrent);	
		}
		catch(Exception e) 
		{
			Log.e("SNIPVIEW", e.toString() + " hashmap error");
			notifyScreenWithToast("hashmap error");
		}
	}
	
	private void addNewSnippet(Snippet newSnippet)
	{
		_snippets.put(newSnippet.getID(), newSnippet);
		_observer.addSnippetView(newSnippet);
	}
	
	private void updateExistingSnippet(Snippet updatedVersion)
	{
		_snippets.get(updatedVersion.getID()).update(updatedVersion);
	}
	
	private void deleteOldSnippets(HashMap<String, Snippet> oldSnippets)
	{		
		
		for(String key : oldSnippets.keySet())
		{
			
			// tell the snippet to alert its related view that he will be deleted soon, then delete him.
			Snippet snippetToDelete = _snippets.get(key);
			Log.d("SNIPVIEW", "rawr - delete " + snippetToDelete.getID() + "... " + oldSnippets.size());
			snippetToDelete.notifyObservers("DELETED");
			_snippets.remove(key);
		}
	}
	
	public void resetTimer()
	{
		_timer.cancel();
		_timer.purge();
		_timer = new Timer();
	}
	
	/*
	 * This method is called when the decoder has scanned a new set of ReferencePoints
	 */
//	public void updateReferencePointList(ArrayList<ReferencePoint> updatedReferencePoints)
//	{
//		//update ReferencePoints
//		Log.d("ONSCREEN", String.format("%d", updatedReferencePoints.size()));
//		if(!updatedReferencePoints.isEmpty() && updatedReferencePoints.size() >= 2 && _screenDimension != null)
//		{
//			// clear the current timer, make a new one and add a task to it.
//			//resetTimer();
//	
////			_timer.schedule(new UpdateTask(), 1000, 1000);
//			
//			this._refPoints = updatedReferencePoints;
//			
//			createConnection();
//		}	
//		else updateSnippetList(new HashMap<String, Snippet>());
//	}
	
	public void updateScreenCoordinates(WorldCoordinate upperLeft, WorldCoordinate lowerRight)
	{
		
		setUpperLeft(upperLeft);
		setLowerRight(lowerRight);
		createConnection();	}
	
	//For testing purposes
	@SuppressWarnings("unused")
	private void drawPoints()
	{
//		ArrayList<Float> points = new ArrayList<Float>();
//		
//		for (ReferencePoint refPoint : _refPoints) 
//		{
//			for (float f : refPoint.getPoints()) 
//			{
//				points.add(f);
//			}
//		}
//		
//		float[] pointArray = new float[points.size()];
//		
//		for (int i = 0; i < points.size(); i++) 
//		{
//			pointArray[i] = points.get(i);
//		}
//		
//		_observer.drawPoints(pointArray);
	}
	
	private void createConnection()
	{
		ServiceConnection serviceConnection = new ServiceConnection();
		serviceConnection.execute();	
	}
	
	
	public WorldCoordinate getUpperLeft() 
	{
		return upperLeft;
	}

	private void setUpperLeft(WorldCoordinate upperLeft) 
	{
		this.upperLeft = upperLeft;
	}

	public WorldCoordinate getLowerRight() 
	{
		return lowerRight;
	}

	private void setLowerRight(WorldCoordinate lowerRight) 
	{
		this.lowerRight = lowerRight;
	}	

	public float getAngle() 
	{
		return angle;
	}

	public void setAngle(float angle) 
	{
		this.angle = angle;
	}

	public void notifyScreenWithToast(String msg)
	{
		Log.e("TOAST", String.format("%s", msg));
		_observer.makeToast(msg);
	}

	public void setObserver(ScannerActivity observer) {
		if(this._observer == null)
			this._observer = observer;
	}
	

//	public ArrayList<ReferencePoint> getReferencePoints()
//	{
//		return _refPoints;
//	}
	
	public Dimension getScreenDimension()
	{
		return _screenDimension;
	}
	
	public void setScreenDimension(int width, int height)
	{
		if(_screenDimension == null)
			_screenDimension = new Dimension(width, height);
	}
	
	private float _leftToRightScale = 0f;
	
	public void setLeftToRightCodeScale(float scale)
	{
		
		this._leftToRightScale = scale;
	}
	
	public float getLeftTorightCodeScale()
	{
		return this._leftToRightScale;
	}
}
